#!/usr/bin/python
# coding=utf-8

"""批量打包

python xbale.py -o pp -c waps -x 1 -u nes -l com.fcgo.mxd2 -b path
"""
__author__ = 'x'
__email__ = 'x@gmail.com'
VERSION = "v1.0"

import os, sys, re, time, getopt, string, datetime
import shutil
import glob
import Image
import ConfigParser
import zipfile
import md5
import json

tihuan = []
info = []

opts, args = getopt.getopt(sys.argv[1:], "hb:p:c:d:v:w:o:a:t:x:u:l:", ["help", "base=", "packname=", "umeng_channel=", "datetime_sluice=", "version_code=", "version_name=", "do=", "apk_type=", "tmpis=", "append=", "app_type=", "old_packname="])
#基目录*
is_has_base_path = False
base_path = ""
#要变成的包名
is_has_packname = False
packname = ""
#渠道
is_has_umeng_channel = False
umeng_channel = ""
#日期,默认30天后
is_has_datetime_sluice = False
datetime_sluice = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time() + 15*24*60*60))
#versionCode
is_has_version_code = False
version_code = time.strftime('%Y%m%d',time.localtime(time.time()))
#versionName
is_has_version_name = False
y_version_name = str(int(time.strftime('%Y',time.localtime(time.time()))) - 2013 + 1)
m_version_name = str(int(time.strftime('%m',time.localtime(time.time()))))
d_version_name = str(int(time.strftime('%d',time.localtime(time.time()))))
version_name = y_version_name+"."+m_version_name+"."+d_version_name
#do something ["pp", "jpp", "dl" ...] 万普、假万普、点金  ...
doit = "pp"
#类型::["bg", "dg", ...]  百度谷歌、多盟谷歌  ...
apk_type = "bg"
#临时打包到的目录名
tmpis = ""
#追加，append等于“1”表示不追加
append = "1"
#app类型
is_has_app_type = False
app_type = "gba"
#母包名
is_has_old_packname = False
old_packname = "com.androidemu.gbaeurdbzb"


#需要替换的文件后缀
ext_name = ""



def usage():
    print ("Usage:xbale.py [ops]")

if not opts:
    usage()
    sys.exit()
else:
    for op, value in opts:
#        print "op:value="+op+":"+value
        if op == "-b" or op == "--base":
            is_has_base_path = True
            base_path = value
        elif op == "-p" or op == "--packname":
            is_has_packname = True
            packname = value
        elif op == "-c" or op == "--umeng_channel":
            is_has_umeng_channel = True
            umeng_channel = value
        elif op == "-d" or op == "--datetime_sluice":
            is_has_datetime_sluice = True
            datetime_sluice = value
        elif op == "-v" or op == "--version_code":
            is_has_version_code = True
            version_code = value
        elif op == "-w" or op == "--version_name":
            is_has_version_name = True
            version_name = value
        elif op == "-o" or op == "--do":
            doit = value
        elif op == "-a" or op == "--apk_type":
            apk_type = value
        elif op == "-t" or op == "--tmpis":
            tmpis = value
        elif op == "-x" or op == "--append":
            append = value
        elif op == "-u" or op == "--app_type":
            is_has_app_type = True
            app_type = value
        elif op == "-l" or op == "--old_packname":
            is_has_old_packname = True
            old_packname = value
        elif op == "-h" or op == "--help":
            usage()
            sys.exit()


text_characters = "".join(map(chr, range(32, 127)) + list("\n\r\t\b"))
_null_trans = string.maketrans("", "")

def istextfile(filename, blocksize = 512):
    fileread = open(filename,'r');
    filer_tmp_str = fileread.read(blocksize);
    fileread.close();
    return istext(filer_tmp_str)

def istext(s):
    if "\0" in s:
        return 0

    if not s:  # Empty files are considered text
        return 1

    # Get the non-text characters (maps a character to itself then
    # use the ‘remove’ option to get rid of the text characters.)
    t = s.translate(_null_trans, text_characters)

    # If more than 30% non-text characters, then
    # this is considered a binary file
    if len(t)/len(s) > 0.30:
        return 0
    return 1

#解压zip文件
def unzip():
    source_zip="c:\\update\\SW_Servers_20120815.zip"
    target_dir="c:\\update\\"
    myzip=ZipFile(source_zip)
    myfilelist=myzip.namelist()
    for name in myfilelist:
        f_handle=open(target_dir+name,"wb")
        f_handle.write(myzip.read(name))
        f_handle.close()
    myzip.close()

#添加文件到已有的zip包中
def addzip():
    f = zipfile.ZipFile('archive.zip','a',zipfile.ZIP_DEFLATED)
    f.write('file_to_add.py')
    f.close()

#把整个文件夹内的文件打包
def adddirfile(the_zipfile, the_dir, the_append_bale_root_path):
    f = zipfile.ZipFile(the_zipfile,'a',zipfile.ZIP_DEFLATED)
    startdir = the_dir
    for dirpath, dirnames, filenames in os.walk(startdir):
        b = dirpath.replace(the_append_bale_root_path,'')
#        strinfo = re.compile('\\bale_need_data')
#        b = strinfo.sub('',dirpath)
#        print "dirpath==="+dirpath
        for filename in filenames:
            b_file = os.path.join(b,filename)
            f.write(os.path.join(dirpath,filename), b_file)
    f.close()

def run_sub(filelist, strregexs, strtos):
    if filelist:
        ssrcs = [];
        for strregex in strregexs:
            ssrcs.append(re.compile(strregex,re.DOTALL));
        for filename in filelist:
            fileread = open(filename,'r');
            filer = fileread.read();
            sub = filer
            need_write = False
            i = 0;
            for ssrc in ssrcs:
                sto = strtos[i];
                m = re.search(ssrc, sub)
                if m:
                    need_write = True
                    sub = re.sub(ssrc,sto,sub,0); #替换sregex 为 sto
                fileread.close();
#                if m:
#                    print strregexs[i];
#                    print filename;
#                    print sub;
#                    break
                i = i + 1;
            if need_write:
                fileok = open(filename,'w');
                fileok.write(sub);
                fileok.close();
#            if i:
#                break




def main():
    '''主函数'''
    old_root_path = "F:\\wwplan\\hack\\me"
    append_bale_root_path = "F:\\wwplan\\hack\\me\\bale_need_data\\bg"
    append_bale_dirs = []
    if apk_type == "bg":
        append_bale_dirs = [append_bale_root_path+"\\extra", append_bale_root_path+"\\jsr305_annotations"]
    elif apk_type == "g":
        append_bale_dirs = [append_bale_root_path+"\\jsr305_annotations"]
    old_path = old_root_path+"\\"+doit+"_"+old_packname
    ngapktool_path = "F:\\wwplan\\hack\\tool\\NgApkTool"
    apk_sign_tool_path = r'"C:\Program Files\Java\jdk1.6.0_25\bin\jarsigner.exe"'
    keystore_path = r"F:\wwplan\data\android_0.keystore"
    private_data = json.loads(open(sys.path[0]+"/private_data/base.json").read())
    keystore_alias = private_data["keystore_alias"]
    storepass = private_data["keystore_storepass"]
    keypass = private_data["keystore_keypass"]
    ini_file_name = "p.ini"

#    adddirfile("tmp.zip", append_bale_dirs[0], append_bale_root_path)
#    sys.exit()

    if not os.path.exists(base_path):
        print "root path not exists"
        sys.exit()

    if not os.path.exists(old_path):
        print "old path not exists"
        sys.exit()

    print "Prophase Preparation..."
    ini_file_path = base_path+"\\"+ini_file_name;
    if not os.path.exists(ini_file_path):
        print "p.ini file is not exists"
        sys.exit()
    config = ConfigParser.SafeConfigParser()
    config.read(ini_file_path)
    tihuan = config.items("tihuan")
    info = config.items("info")
#    if len(tihuan) < 8:
#        print ini_file_name+" info is wrong!"
#        sys.exit()
#    print tihuan
#    print info
    #限定需要读在线配置的日期
    datetime_sluice_last = datetime_sluice
    if not is_has_datetime_sluice and config.has_option('info', 'datetime_sluice'):
        datetime_sluice_last = config.get('info', 'datetime_sluice')
    #要变成的包名
    packname_last = packname
    if not is_has_packname and config.has_option('info', 'packname'):
        packname_last = config.get('info', 'packname')
    #要打的渠道list
    umeng_channel_last = umeng_channel
    if not is_has_umeng_channel:
        if doit != "" and config.has_option('info', doit+'_umeng_channel'):
            umeng_channel_last = config.get('info', doit+'_umeng_channel')
        elif config.has_option('info', 'umeng_channel'):
            umeng_channel_last = config.get('info', 'umeng_channel')
    if umeng_channel_last != "":
        if umeng_channel_last.find(",") != -1:
            umeng_channel_list = umeng_channel_last.split(",")
        else:
            umeng_channel_list = umeng_channel_last.split("|")
    else:
        print "channel is empty!"
        sys.exit()
    #版本号
    version_code_last = version_code
    if not is_has_version_code:
        if doit != "" and config.has_option('info', doit+'_version_code'):
            version_code_last = config.get('info', doit+'_version_code')
        elif config.has_option('info', 'version_code'):
            version_code_last = config.get('info', 'version_code')
    #版本名
    version_name_last = version_name
    if not is_has_version_name:
        if doit != "" and config.has_option('info', doit+'_version_name'):
            version_name_last = config.get('info', doit+'_version_name')
        elif config.has_option('info', 'version_name'):
            version_name_last = config.get('info', 'version_name')
    is_change_packname = True
    if packname_last == old_packname:
        is_change_packname = False
    #app类型
    app_type_last = app_type
    if not is_has_app_type:
        if doit != "" and config.has_option('info', doit+'_app_type'):
            app_type_last = config.get('info', doit+'_app_type')
        elif config.has_option('info', 'app_type'):
            app_type_last = config.get('info', 'app_type')

    #制造打包基础存放路径
    tmp_str = base_path
    base_path_pardir = os.path.dirname(base_path);
    key = md5.new()
    key.update(base_path_pardir)
    base_path_pardir_md5 = key.hexdigest()
    tmp_str = tmp_str.replace(":", "")
    tmp_str_list = tmp_str.split("\\")
    apk_dir_base_path = ""
    unique_dir_name = ""
    for item in tmp_str_list:
        unique_dir_name = item + "~" + unique_dir_name
    if unique_dir_name != "":
        unique_dir_name = base_path_pardir_md5[0:2] + "@" + unique_dir_name[:-1]
#    apk_dir_base_path = tmp_str.replace("\\", "~");
    apk_dir_base_path = r"F:\wwplan\market" + "\\" + app_type_last + "\\" + unique_dir_name;
    #打包过程产生的临时文件存放的位置
    nw_dir_base_path = r"F:\wwplan\hack\bale_tmp" + "\\" + app_type_last + "\\" + unique_dir_name;

    #存放最终打包的apk目录
    apk_dir = apk_dir_base_path+"\\apk_"+doit
    #如果是append=2表示追加，已经存在的就跳过
    if (append == "2" and os.path.exists(apk_dir)):
        apk_dir_list = os.listdir(apk_dir)
        #如果apk个数跟渠道数不一样就重新打包该目录下的apk
        if len(apk_dir_list) == len(umeng_channel_list):
            is_skip = True
            for item in apk_dir_list:
                tmp = os.path.splitext(item);
                if tmp[1] in [".tmp"]:
                    is_skip = False
            if is_skip:
                print "skip this "+apk_dir
                sys.exit()
            else:
                shutil.rmtree(apk_dir)
        else:
            shutil.rmtree(apk_dir)
    copy_filelist = [];
    for i in os.listdir(base_path):
        j = os.path.splitext(i);
        if i in ["app_icon.png", "gba_bios.bin", "wlmj.html", "wlmj_files"]:
            copy_filelist.append(i);
        if j[1] in ["."+app_type_last]:
            copy_filelist.append(i);
#    print copy_filelist
#    sys.exit()

    nw_dir = nw_dir_base_path + "\\"+doit+"_"+packname_last
    if os.path.exists(nw_dir):
        shutil.rmtree(nw_dir)
    shutil.copytree(old_path, nw_dir)

    c1 = os.walk(nw_dir);
    filelist = [];
    for c2 in c1:
        for c3 in c2[2]:
            c4 = os.path.join(c2[0],c3);
            c5 = os.path.splitext(c4);
            if istextfile(c4):
                if (ext_name == "*" or ext_name == "") or c5[1] == ext_name:
                    filelist.append(c4);
                else:
                    pass;
#    print filelist;

    #如果包名一样就不用替换包名相关字符串
    if is_change_packname:
        #改变包名，替换包名相关字符串
        items0 = old_packname.split(".")
        items1 = packname_last.split(".")
        strregexs = ["\.".join(items0), "/".join(items0), "_".join(items0), "-".join(items0)]
        strtos = [".".join(items1), "/".join(items1), "_".join(items1), "-".join(items1)]
    else:
        strregexs = []
        strtos = []

    #根据读取p.ini的tihuan list
    for i in tihuan:
        strregexs.append("{@"+i[0]+"@}")
        strtos.append(i[1])
    strregexs.append("{@datetime_sluice@}")
    strtos.append(datetime_sluice_last)
#    print strregexs
#    print strtos

    #多文件替换
    run_sub(filelist, strregexs, strtos)

    #拷贝app_icon.png/wlmj.html/rom_file.gba/gba_bios.bin进去
    for i in copy_filelist:
        j = os.path.splitext(i);
        src_path = base_path+"\\"+i
        to_path = nw_dir+"\\assets\\"+i
        if i == "app_icon.png":
            to_path = nw_dir+"\\res\\drawable\\"+i
        elif j[1] in ["."+app_type_last]:
            to_path = nw_dir+"\\assets\\rom_file."+app_type_last
        if os.path.isdir(src_path):
            shutil.copytree(src_path, to_path)
        else:
            shutil.copyfile(src_path, to_path)

    #如果包名一样，就不用改变包路径
    if is_change_packname:
        #改变包路径
        src_dir = nw_dir + "\\smali\\" + "\\".join(items0)
        to_dir = nw_dir + "\\smali\\" + "\\".join(items1)
        if os.path.exists(src_dir):
            if os.path.exists(to_dir):
                shutil.rmtree(to_dir)
            shutil.copytree(src_dir, to_dir)
            shutil.rmtree(src_dir)

    bak_dir = nw_dir_base_path + "\\bak_"+doit
    needbak_files = [nw_dir+"\\AndroidManifest.xml"]
    bak_files = [bak_dir + "\\AndroidManifest.xml"]

    #单文件替换
    run_sub([needbak_files[0]], ["{@version_code@}", "{@version_name@}"], [version_code_last, version_name_last])

    if os.path.exists(bak_dir):
        shutil.rmtree(bak_dir)
    os.mkdir(bak_dir)
    c = len(needbak_files);
    for i in range(0, c):
        shutil.copyfile(needbak_files[i], bak_files[i])

    print "Baling..."

    #如果没有特别指定渠道id，就判断是否是追加。如果append=1表示不追加就删除apk_dir
    #如果appen=3就表示追加好了（因为上面已经判断了append=2的情况），不删除apk_dir
    if not is_has_umeng_channel:
        #append等于“1”表示不追加
        if append == "1":
            if os.path.exists(apk_dir):
                shutil.rmtree(apk_dir)
    if not os.path.exists(apk_dir):
        try :
            os.makedirs(apk_dir)
        except OSError, why :
            print "Faild: %s " % str(why)
    #最后开始循环打多渠道包
    for i in umeng_channel_list:
        fileread = open(bak_files[0],'r');
        sub = fileread.read();
        fileread.close();
        sub = re.sub(re.compile("{@umeng_channel@}",re.DOTALL), i, sub, 0);
#        fileok = open(nw_dir+"\\AndroidManifest_"+i+".xml",'w');
        fileok = open(nw_dir+"\\AndroidManifest.xml",'w');
        fileok.write(sub);
        fileok.close();
        #打包
        print ""+i+" baling..."
        apk_last = apk_dir+"\\"+doit+"_"+packname_last+"_"+i+"_"+version_name_last+"_"+version_code_last+".apk"
        apktool_ops = "-f"
        x1 = nw_dir
        x2 = apk_last+".tmp"
        os.system('java -jar '+ngapktool_path+'\\apktool.jar b %s %s %s' % (apktool_ops, x1, x2))
        for i_i in append_bale_dirs:
            adddirfile(x2, i_i, append_bale_root_path)
#        os.system('java -jar '+ngapktool_path+'\\signapk.jar '+ngapktool_path+'\\testkey.x509.pem '+ngapktool_path+'\\testkey.pk8 %s %s' % (x2, apk_last))
#        os.system(apk_sign_tool_path+" -verbose -keystore "+keystore_path+" -storepass "+storepass+" -keypass "+keypass+" -signedjar %s %s %s" % (apk_last, x2, keystore_alias))
        os.system(apk_sign_tool_path+" -keystore "+keystore_path+" -storepass "+storepass+" -keypass "+keypass+" -signedjar %s %s %s" % (apk_last, x2, keystore_alias))
        if os.path.exists(x2):
            os.remove(x2)
        print i+" ok!"

#    if os.path.exists(nw_dir):
#        shutil.rmtree(nw_dir)
#    if os.path.exists(bak_dir):
#        shutil.rmtree(bak_dir)
    if os.path.exists(nw_dir_base_path):
        try:
            shutil.rmtree(nw_dir_base_path)
        except OSError, why :
            print "Faild: %s " % str(why)

    print "Good job!"

if __name__ == "__main__":
  sys.exit(main())
